/*
@title: Basic Tetris
@description: "Basic Tetris" by Ivan Jehad is a slightly more challenging version of classic Tetris, where tetrominos are not complete blocks, making it tricky to determine how they fit into spaces.
@author: Ivan-Jehad
@tags: []
@addedOn: 2024-06-10
@img: ""
*/

const empty = "e";
const block = "b";

// Tetriminos (I, O, T, S, Z, J, L)
const tetriminos = [
  [[1, 1, 1, 1]], // I
  [[1, 1], [1, 1]], // O
  [[1, 1, 1], [0, 1, 0]], // T
  [[0, 1, 1], [1, 1, 0]], // S
  [[1, 1, 0], [0, 1, 1]], // Za
  [[1, 0, 0], [1, 1, 1]], // J
  [[0, 0, 1], [1, 1, 1]]  // L
];

const boardWidth = 10;
const boardHeight = 16;
let currentPiece;
let currentX = 3;
let currentY = 0;
let heldPiece = null;
let canHold = true;
let board = Array.from({ length: boardHeight }, () => Array(boardWidth).fill(empty));
let score = 0;
let interval = 1000;
let gameInterval;

const blockColors = [
  color`1`, // Red for I
  color`2`, // Green for O
  color`3`, // Blue for T
  color`4`, // Yellow for S
  color`5`, // Purple for Z
  color`6`, // Cyan for J
  color`7`  // Orange for L
];

setLegend(
  [empty, bitmap`
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................`],
  [block, bitmap`
................
................
......1111......
......1111......
......1111......
......1111......
......1111......
......1111......
......1111......
......1111......
......1111......
......1111......
......1111......
......1111......
................
................`],
  [block, bitmap`
................
................
......2222......
......2222......
......2222......
......2222......
......2222......
......2222......
......2222......
......2222......
......2222......
......2222......
......2222......
......2222......
................
................`],
  [block, bitmap`
................
................
......3333......
......3333......
......3333......
......3333......
......3333......
......3333......
......3333......
......3333......
......3333......
......3333......
......3333......
......3333......
................
................`],
  [block, bitmap`
................
................
......4444......
......4444......
......4444......
......4444......
......4444......
......4444......
......4444......
......4444......
......4444......
......4444......
......4444......
......4444......
................
................`],
  [block, bitmap`
................
................
......5555......
......5555......
......5555......
......5555......
......5555......
......5555......
......5555......
......5555......
......5555......
......5555......
......5555......
......5555......
................
................`],
  [block, bitmap`
................
................
......6666......
......6666......
......6666......
......6666......
......6666......
......6666......
......6666......
......6666......
......6666......
......6666......
......6666......
......6666......
................
................`],
  [block, bitmap`
................
................
......7777......
......7777......
......7777......
......7777......
......7777......
......7777......
......7777......
......7777......
......7777......
......7777......
......7777......
......7777......
................
................`]
);


function drawBoard() {
  let mapString = board.map(row => row.join('')).join('\n');
  setMap(mapString);

  // Draw the current piece
  for (let row = 0; row < currentPiece.length; row++) {
    for (let col = 0; col < currentPiece[row].length; col++) {
      if (currentPiece[row][col]) {
        addSprite(currentX + col, currentY + row, block);
      }
    }
  }
}

function newPiece() {
  currentPiece = tetriminos[Math.floor(Math.random() * tetriminos.length)];
  currentX = Math.floor((boardWidth - currentPiece[0].length) / 2);
  currentY = 0;
  canHold = true;
  if (!pieceFits(currentX, currentY, currentPiece)) {
    // Game over condition: piece cannot fit at the starting position
    board = Array.from({ length: boardHeight }, () => Array(boardWidth).fill(block));
    drawBoard();
    setTimeout(() => {
      board = Array.from({ length: boardHeight }, () => Array(boardWidth).fill(empty));
      score = 0;
      interval = 1000;
      newPiece();
      drawBoard();
      clearInterval(gameInterval);
      gameInterval = setInterval(update, interval);
    }, 2000);
  }
}

function pieceFits(x, y, piece) {
  for (let row = 0; row < piece.length; row++) {
    for (let col = 0; col < piece[row].length; col++) {
      if (piece[row][col] && (board[y + row] && board[y + row][x + col]) !== empty) {
        return false;
      }
    }
  }
  return true;
}

function placePiece() {
  for (let row = 0; row < currentPiece.length; row++) {
    for (let col = 0; col < currentPiece[row].length; col++) {
      if (currentPiece[row][col]) {
        board[currentY + row][currentX + col] = block;
      }
    }
  }
}

function rotatePiece(piece) {
  const newPiece = [];
  for (let col = 0; col < piece[0].length; col++) {
    const newRow = [];
    for (let row = piece.length - 1; row >= 0; row--) {
      newRow.push(piece[row][col]);
    }
    newPiece.push(newRow);
  }
  return newPiece;
}

function clearLines() {
  let linesCleared = 0;
  for (let row = boardHeight - 1; row >= 0; row--) {
    if (board[row].every(cell => cell === block)) {
      board.splice(row, 1);
      board.unshift(Array(boardWidth).fill(empty));
      row++;
      linesCleared++;
    }
  }
  if (linesCleared > 0) {
    score += linesCleared * 100;
    interval = Math.max(100, interval - linesCleared * 50); // Increase speed
    clearInterval(gameInterval);
    gameInterval = setInterval(update, interval);
  }
}

function update() {
  if (pieceFits(currentX, currentY + 1, currentPiece)) {
    currentY++;
  } else {
    placePiece();
    clearLines();
    newPiece();
  }
  drawBoard();
  displayScore();
}

function displayScore() {
  clearText();
  addText(`${score}`, { x: 1, y: 2, color: color`3`,size: 0.1 });
}

newPiece();
drawBoard();
displayScore();

onInput("a", () => {
  if (pieceFits(currentX - 1, currentY, currentPiece)) {
    currentX--;
    drawBoard();
    displayScore();
  }
});

onInput("d", () => {
  if (pieceFits(currentX + 1, currentY, currentPiece)) {
    currentX++;
    drawBoard();
    displayScore();
  }
});

onInput("s", () => {
  if (pieceFits(currentX, currentY + 1, currentPiece)) {
    currentY++;
    drawBoard();
    displayScore();
  }
});

onInput("w", () => {
  const rotatedPiece = rotatePiece(currentPiece);
  if (pieceFits(currentX, currentY, rotatedPiece)) {
    currentPiece = rotatedPiece;
    drawBoard();
    displayScore();
  }
});

onInput("j", () => {
  if (canHold) {
    if (heldPiece) {
      const temp = currentPiece;
      currentPiece = heldPiece;
      heldPiece = temp;
      currentX = Math.floor((boardWidth - currentPiece[0].length) / 2);
      currentY = 0;
    } else {
      heldPiece = currentPiece;
      newPiece();
    }
    canHold = false;
    drawBoard();
    displayScore();
  }
});

gameInterval = setInterval(update, interval);
